#pragma once

#include "Utils.h"
#include "Texture.h"
#include <qlistwidget.h>


enum class materialType
{
	FLT = 0,
	DFS,
	MIR,
	MTL,
	EMT
};

class material
{
public:
	texture* color;
	virtual scatterInfo scatter(const hitInfo& hi, const Ray& ray)const = 0;
	materialType type;
	QString name;
};


class Diffuse : public material
{
public:
	Diffuse(QString name, texture* color) { this->color = color; this->name = name; type = materialType::DFS; }

	scatterInfo scatter(const hitInfo& hi, const Ray& ray)const
	{
		//vec lv = vec::crossProduct(hi.normal, ray.dir).normalized();
		//vec fv = vec::crossProduct(lv, hi.normal).normalized();
		vec rv = randvec();
		//while (vec::dotProduct(rv, hi.normal) < 0)
		//{
		//	rv = randvec();
		//}
		vec nv = rv + hi.normal;

		vec surp = hi.pos + hi.normal * .00003;
		Ray oRay(surp, nv);
		float mult = vec::dotProduct(oRay.dir, hi.normal);
		vec gcolor = color->value(vec2(0, 0), hi, ray);

		return scatterInfo(oRay, mult * gcolor);
	}
};

class Flat : public material
{
public:
	Flat(QString name, texture* color) { this->color = color; this->name = name; type = materialType::FLT; }

	scatterInfo scatter(const hitInfo& hi, const Ray& ray)const
	{
		vec surp = hi.pos + hi.normal * .00003;
		float sn = vec::dotProduct(ray.dir, hi.normal * -1);
		vec rv = ray.dir + hi.normal * 2 * sn;

		return scatterInfo(ray, color->value(vec2(0, 0), hi, ray));
	}
};

class Mirror : public material
{
public:
	Mirror(QString name) { type = materialType::MIR; this->name = name; }
	scatterInfo scatter(const hitInfo& hi, const Ray& ray)const
	{
		vec surp = hi.pos + hi.normal * .00003;
		float sn = vec::dotProduct(ray.dir, hi.normal * -1);
		vec rv = ray.dir + hi.normal * 2 * sn;
		return scatterInfo(Ray(surp, rv), vec(1, 1, 1));
	}
};

class Metal : public material
{
public:
	float roughness;
	Metal(QString name, texture* color, float roughness = 0) :roughness(roughness) { this->color = color; this->name = name; type = materialType::MTL; }
	scatterInfo scatter(const hitInfo& hi, const Ray& ray)const
	{
		float sn = vec::dotProduct(ray.dir, hi.normal * -1);
		vec rv = ray.dir + hi.normal * 2 * sn;
		if (roughness > 0) {
			float maxr = vec::dotProduct(hi.normal, rv);
			float nrough = roughness;
			if (roughness > maxr)
			{
				nrough = maxr;
			}
			rv += randvec() * nrough;
		}
		vec gcolor = color->value(vec2(0, 0), hi, ray);
		vec surp = hi.pos + hi.normal * .00003;
		return scatterInfo(Ray(surp, rv), gcolor);

	}

};

class Emission : public material
{
public:
	float scale;
	Emission(QString name, texture* color, float scale = 2) :
		scale(scale) {
		this->color = color;
		this->name = name;
		type = materialType::EMT;
	}
	scatterInfo scatter(const hitInfo& hi, const Ray& ray)const {
		vec gcolor = color->value(vec2(0,0), hi, ray);
		return scatterInfo(Ray(), gcolor * scale);
	}
};

extern material* defaultMat;

extern QList<material*> matList;